//
//  PMSlideJoint.m
//  PezMonkey
//
//  Created by Spence DiNicolantonio on 10/31/09.
//  Copyright 2009 Necrosoft. All rights reserved.
//

/**
 * A subclass of PMConstraint for representing slide joints. PMSlideJoints are 
 * like PMPinJoints, but have flexible anchors. A limit is used to constrain the 
 * anchors freedom to a particular range. This range define the minimum and 
 * maximum distance that a joint anchor can slide from the set anchor point.
 */

#import "PMSlideJoint.h"
#import "PMBody.h"


@implementation PMSlideJoint


#pragma mark -
#pragma mark Init

/**
 * Returns a PMPinJoint object initialized by anchoring two given rigid bodies at 
 * their center of gravity, with slide limits defined by a given range.
 */
- (id)initWithBodyA:(PMBody *)aBody bodyB:(PMBody *)otherBody limit:(PMRange)limit {
	return [self initWithAnchor:PMVectZero 
						onBodyA:aBody 
						 anchor:PMVectZero 
						onBodyB:otherBody
						  limit:limit];
}

/**
 * Returns a PMPinJoint object initialized by anchoring two given rigid bodies at 
 * given positions relative to the corresponding body's center of gravity. The 
 * slide limits are set to a given range.
 */
- (id)initWithAnchor:(PMAnchor)anchorA onBodyA:(PMBody *)aBody anchor:(PMAnchor)anchorB onBodyB:(PMBody *)otherBody limit:(PMRange)limit { 
	if (![super init])
		return nil;
	
	// allocate and initialize cpConstraint
	constraint = cpSlideJointNew(aBody.cpBody, otherBody.cpBody, anchorA, anchorB, limit.min, limit.max);
	
	// set body references
	self.bodyA = aBody;
	self.bodyB = otherBody;
	
	// reference this PMConstraint object with cpConstraint's data pointer
	// this is for internal use
	constraint->data = self;
	
	return self;
}


#pragma mark Convenience

/**
 * Creates and returns a PMPinJoint object initialized by anchoring two given 
 * rigid bodies at their center of gravity, with slide limits defined by a given
 * range.
 */
+ (PMSlideJoint *)slideJointWithBodyA:(PMBody *)aBody bodyB:(PMBody *)otherBody limit:(PMRange)limit {
	return [[[PMSlideJoint alloc] initWithBodyA:aBody 
										  bodyB:otherBody 
										  limit:limit] autorelease];
}

/**
 * Creates and returns a PMPinJoint object initialized by anchoring two given 
 * rigid bodies at given positions reletive to the corresponding body's center 
 * of gravity. The slide limits are set to a given range.
 */
+ (PMSlideJoint *)slideJointWithAnchor:(PMAnchor)anchorA onBodyA:(PMBody *)aBody anchor:(PMAnchor)anchorB onBodyB:(PMBody *)otherBody limit:(PMRange)limit {
	return [[[PMSlideJoint alloc] initWithAnchor:anchorA 
										 onBodyA:aBody 
										  anchor:anchorB 
										 onBodyB:otherBody 
										   limit:limit] autorelease];
}


#pragma mark -
#pragma mark Getters

/**
 * Returns the receiver's joint anchor position for bodyA.
 */
- (PMAnchor)anchorA {
	return cpSlideJointGetAnchr1(constraint);
}

/**
 * Returns the receiver's joint anchor position for bodyB.
 */
- (PMAnchor)anchorB {
	return cpSlideJointGetAnchr2(constraint);
}

/**
 * Returns the receiver's joint anchor position for the body corresponding to the 
 * given key. Returns PMVectZero if there is no body/anchor for the given key.
 */
- (PMAnchor)anchorForBodyWithKey:(PMConstraintBodyKey)bodyKey {
	switch (bodyKey) {
		case PMConstraintBodyKeyA:
			return [self anchorA];
		case PMConstraintBodyKeyB:
			return [self anchorB];
		default:
			return PMVectZero;
	}
}

/**
 * Returns a PMRange defining the allowed distance between the receiver's anchors.
 */
- (PMRange)limit {
	PMFloat min = cpSlideJointGetMin(constraint);
	PMFloat max = cpSlideJointGetMax(constraint);
	return PMRangeMake(min, max);
}


#pragma mark -
#pragma mark Setters

/**
 * Sets the joint anchor position of the receiver's bodyA to a given point relative
 * the the body's center of gravity.
 */
- (void)setAnchorA:(PMAnchor)anchor {
	cpSlideJointSetAnchr1(constraint, anchor);
}

/**
 * Sets the joint anchor position of the receiver's bodyB to a given point relative
 * the the body's center of gravity.
 */
- (void)setAnchorB:(PMAnchor)anchor {
	cpSlideJointSetAnchr2(constraint, anchor);
}

/**
 * Sets the joint anchor position of the receiver's body corresponding to the given
 * key, to a given point relative the the body's center of gravity.
 */
- (void)setAnchor:(PMAnchor)anchor forBodyWithKey:(PMConstraintBodyKey)bodyKey {
	switch (bodyKey) {
		case PMConstraintBodyKeyA:
			return [self setAnchorA:anchor];
		case PMConstraintBodyKeyB:
			return [self setAnchorB:anchor];
	}
}

/**
 * Sets the distance allowed between the receiver's anchors to a given range.
 */
- (void)setLimit:(PMRange)limit {
	cpSlideJointSetMin(constraint, limit.min);
	cpSlideJointSetMax(constraint, limit.max);
}

@end
